package web

import (
	"encoding/json"
	"fmt"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"

	"gddgame.cc/galaxy/server/web/middleware"

	"gddgame.cc/galaxy/utils/def"

	"github.com/go-chi/chi"
)

const (
	defaultMaxMemory = 32 << 20 // 32 MB
)

type MapValues map[string]map[string][]string

type contextInline struct {
	def.Logger
	def.Store

	id string

	isClose bool

	request       *http.Request
	responseWrite http.ResponseWriter
	context       *chi.Context
	Response      interface{}

	queryForm url.Values

	queryMapForm MapValues
	postMapForm  MapValues
}

type Context = middleware.Context

func NewContext() Context {
	return &contextInline{}
}

func (c *contextInline) SetID(id string) {
	c.id = id
}

func (c *contextInline) ID() string {
	return c.id
}

func (c *contextInline) Reset() {
	c.queryForm = nil
	c.postMapForm = nil
	c.queryMapForm = nil
	c.isClose = false
}

func (c *contextInline) Request() *http.Request {
	return c.request
}

func (c *contextInline) Writer() http.ResponseWriter {
	return c.responseWrite
}

func (c *contextInline) IsClose() bool {
	return c.isClose
}

func (c *contextInline) PathParam(key string) string {
	str, _ := url.QueryUnescape(c.context.URLParam(key))
	str, _ = url.QueryUnescape(str)
	return str
}

func (c *contextInline) GetParam(key string) (string, error) {
	if vs, err := c.GetParamArray(key); err != nil {
		return "", err
	} else {
		return vs[0], nil
	}
}

func (c *contextInline) GetParamArray(key string) ([]string, error) {
	err := c.initQuery()
	if err != nil {
		return nil, err
	}
	if vs := c.queryForm[key]; len(vs) > 0 {
		return vs, nil
	} else {
		return nil, fmt.Errorf("%s is not exists", key)
	}
}

func (c *contextInline) GetParamMap(key string) (map[string][]string, error) {
	err := c.initQuery()
	if err != nil {
		return nil, err
	}
	if c.queryMapForm == nil {
		c.queryMapForm = c.extendUrlValues(c.queryForm)
	}
	if vs, ok := c.queryMapForm[key]; ok {
		return vs, nil
	} else {
		return nil, fmt.Errorf("%s is not exists", key)
	}
}

func (c *contextInline) PostParam(key string) (string, error) {
	if vs, err := c.PostParamArray(key); err != nil {
		return "", fmt.Errorf("empty")
	} else {
		return vs[0], nil
	}
}

func (c *contextInline) PostParamArray(key string) ([]string, error) {
	err := c.initPost()
	if err != nil {
		return nil, err
	}
	if vs := c.request.PostForm[key]; len(vs) > 0 {
		return vs, nil
	} else {
		return nil, fmt.Errorf("%s is not exists", key)
	}
}

func (c *contextInline) PostParamMap(key string) (map[string][]string, error) {
	err := c.initPost()
	if err != nil {
		return nil, err
	}
	if c.postMapForm == nil {
		c.postMapForm = c.extendUrlValues(c.request.PostForm)
	}
	if vs, ok := c.postMapForm[key]; ok {
		return vs, nil
	} else {
		return nil, fmt.Errorf("%s is not exists", key)
	}
}

func (c *contextInline) Header(key string) string {
	return c.request.Header.Get(key)
}

func (c *contextInline) Cookie(key string) (*http.Cookie, error) {
	return c.request.Cookie(key)
}

func (c *contextInline) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
	return c.request.FormFile(key)
}

func (c *contextInline) BodyJson(s interface{}) error {
	if err := json.NewDecoder(c.request.Body).Decode(s); err != nil {
		return err
	}
	return nil
}

func (c *contextInline) SetResponse(s interface{}) {
	c.Response = s
}

func (c *contextInline) GetResponse() interface{} {
	return c.Response
}

func (c *contextInline) SetLogger(logger def.Logger) {
	c.Logger = logger
}

func (c *contextInline) SetStore(store def.Store) {
	c.Store = store
}

func (c *contextInline) initQuery() error {
	if c.queryForm == nil {
		queryForm, err := url.ParseQuery(c.request.URL.RawQuery)
		if err != nil {
			return fmt.Errorf("url Parse error:%s", err)
		} else {
			c.queryForm = queryForm
		}
	}
	return nil
}

func (c *contextInline) initPost() error {
	if c.request.Form == nil {
		if err := c.request.ParseMultipartForm(defaultMaxMemory); err != nil {
			return err
		}
	}
	return nil
}

func (c *contextInline) extendUrlValues(form url.Values) MapValues {
	tmp := MapValues{}
	for key, value := range form {
		start := strings.Index(key, "[")
		if start <= 0 {
			continue
		}
		end := strings.Index(key, "]")
		if end <= 0 || end < start {
			continue
		}
		k := key[0:start]
		v, ok := tmp[k]
		if !ok {
			v = make(map[string][]string)
			tmp[k] = v
		}
		kk := key[start+1 : end]
		v[kk] = value
	}
	return tmp
}
